/*
Copyright (C) 1997-2001 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

// Created on 02.02.2004 by RST.
// $Id: MD4.java,v 1.2 2004/07/09 06:50:49 hzi Exp $
using System;
//UPGRADE_TODO: The type 'java.nio.ByteBuffer' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteBuffer = java.nio.ByteBuffer;
//UPGRADE_TODO: The type 'java.nio.ByteOrder' could not be found. If it was not included in the conversion, there may be compiler issues. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1262'"
using ByteOrder = java.nio.ByteOrder;
using Suake2.UI;
using Suake2.UI.client;
using Suake2.UI.game;
using Suake2.UI.render;
using Suake2.UI.server;
using Lib = Suake2.UI.util.Lib;
namespace Suake2.UI.qcommon
{
	
	
	public class MD4:SupportClass.MessageDigestSupport, System.ICloneable
	{
		// MD4 specific object variables
		//...........................................................................
		
		/// <summary> The size in bytes of the input block to the tranformation algorithm.</summary>
		private const int BLOCK_LENGTH = 64; //    = 512 / 8;
		
		/// <summary> 4 32-bit words (interim result)</summary>
		private int[] context = new int[4];
		
		/// <summary> Number of bytes processed so far mod. 2 power of 64.</summary>
		private long count;
		
		/// <summary> 512 bits input buffer = 16 x 32-bit words holds until reaches 512 bits.</summary>
		private sbyte[] buffer = new sbyte[BLOCK_LENGTH];
		
		/// <summary> 512 bits work buffer = 16 x 32-bit words</summary>
		private int[] X = new int[16];
		
		// Constructors
		//...........................................................................
		
		public MD4():base("MD4")
		{
			engineReset();
		}
		
		/// <summary>    This constructor is here to implement cloneability of this class.</summary>
		private MD4(MD4 md):this()
		{
			context = new int[md.context.Length];
			md.context.CopyTo(context, 0);
			buffer = new sbyte[md.buffer.Length];
			md.buffer.CopyTo(buffer, 0);
			count = md.count;
		}
		
		// Cloneable method implementation
		//...........................................................................
		
		/// <summary> Returns a copy of this MD object.</summary>
		//UPGRADE_NOTE: The equivalent of method 'java.security.MessageDigest.clone' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
		public System.Object Clone()
		{
			return new MD4(this);
		}
		
		// JCE methods
		//...........................................................................
		
		/// <summary> Resets this object disregarding any temporary data present at the
		/// time of the invocation of this call.
		/// </summary>
		//UPGRADE_NOTE: The equivalent of method 'java.security.MessageDigestSpi.engineReset' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
		public void  engineReset()
		{
			// initial values of MD4 i.e. A, B, C, D
			// as per rfc-1320; they are low-order byte first
			context[0] = 0x67452301;
			context[1] = unchecked((int) 0xEFCDAB89);
			context[2] = unchecked((int) 0x98BADCFE);
			context[3] = 0x10325476;
			count = 0L;
			for (int i = 0; i < BLOCK_LENGTH; i++)
				buffer[i] = 0;
		}
		
		/// <summary> Continues an MD4 message digest using the input byte.</summary>
		//UPGRADE_NOTE: The equivalent of method 'java.security.MessageDigestSpi.engineUpdate' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
		public void  engineUpdate(sbyte b)
		{
			// compute number of bytes still unhashed; ie. present in buffer
			int i = (int) (count % BLOCK_LENGTH);
			count++; // update number of bytes
			buffer[i] = b;
			if (i == BLOCK_LENGTH - 1)
				transform(buffer, 0);
		}
		
		/// <summary> MD4 block update operation.
		/// <p>
		/// Continues an MD4 message digest operation, by filling the buffer,
		/// transform(ing) data in 512-bit message block(s), updating the variables
		/// context and count, and leaving (buffering) the remaining bytes in buffer
		/// for the next update or finish.
		/// 
		/// </summary>
		/// <param name="input">   input block
		/// </param>
		/// <param name="offset">   start of meaningful bytes in input
		/// </param>
		/// <param name="len">       count of bytes in input block to consider
		/// </param>
		//UPGRADE_NOTE: The equivalent of method 'java.security.MessageDigestSpi.engineUpdate' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
		public void  engineUpdate(sbyte[] input, int offset, int len)
		{
			// make sure we don't exceed input's allocated size/length
			if (offset < 0 || len < 0 || (long) offset + len > input.Length)
				throw new System.IndexOutOfRangeException();
			
			// compute number of bytes still unhashed; ie. present in buffer
			int bufferNdx = (int) (count % BLOCK_LENGTH);
			count += len; // update number of bytes
			int partLen = BLOCK_LENGTH - bufferNdx;
			int i = 0;
			if (len >= partLen)
			{
				Array.Copy(input, offset, buffer, bufferNdx, partLen);
				
				transform(buffer, 0);
				
				for (i = partLen; i + BLOCK_LENGTH - 1 < len; i += BLOCK_LENGTH)
					transform(input, offset + i);
				bufferNdx = 0;
			}
			// buffer remaining input
			if (i < len)
				Array.Copy(input, offset + i, buffer, bufferNdx, len - i);
		}
		
		/// <summary> Completes the hash computation by performing final operations such
		/// as padding. At the return of this engineDigest, the MD engine is
		/// reset.
		/// 
		/// </summary>
		/// <returns> the array of bytes for the resulting hash value.
		/// </returns>
		//UPGRADE_NOTE: The equivalent of method 'java.security.MessageDigestSpi.engineDigest' is not an override method. "ms-help://MS.VSCC.v80/dv_commoner/local/redirect.htm?index='!DefaultContextWindowIndex'&keyword='jlca1143'"
		public sbyte[] engineDigest()
		{
			// pad output to 56 mod 64; as RFC1320 puts it: congruent to 448 mod 512
			int bufferNdx = (int) (count % BLOCK_LENGTH);
			int padLen = (bufferNdx < 56)?(56 - bufferNdx):(120 - bufferNdx);
			
			// padding is alwas binary 1 followed by binary 0s
			sbyte[] tail = new sbyte[padLen + 8];
			tail[0] = (sbyte) SupportClass.Identity(0x80);
			
			// append length before final transform:
			// save number of bits, casting the long to an array of 8 bytes
			// save low-order byte first.
			for (int i = 0; i < 8; i++)
				tail[padLen + i] = (sbyte) (SupportClass.URShift((count * 8), (8 * i)));
			
			engineUpdate(tail, 0, tail.Length);
			
			sbyte[] result = new sbyte[16];
			// cast this MD4's context (array of 4 ints) into an array of 16 bytes.
			for (int i = 0; i < 4; i++)
				for (int j = 0; j < 4; j++)
					result[i * 4 + j] = (sbyte) (SupportClass.URShift(context[i], (8 * j)));
			
			// reset the engine
			engineReset();
			return result;
		}
		
		// own methods
		//...........................................................................
		
		/// <summary>    MD4 basic transformation.
		/// <p>
		/// Transforms context based on 512 bits from input block starting
		/// from the offset'th byte.
		/// 
		/// </summary>
		/// <param name="block">   input sub-array.
		/// </param>
		/// <param name="offset">   starting position of sub-array.
		/// </param>
		private void  transform(sbyte[] block, int offset)
		{
			
			// encodes 64 bytes from input block into an array of 16 32-bit
			// entities. Use A as a temp var.
			for (int i = 0; i < 16; i++)
				X[i] = (block[offset++] & 0xFF) | (block[offset++] & 0xFF) << 8 | (block[offset++] & 0xFF) << 16 | (block[offset++] & 0xFF) << 24;
			
			int A = context[0];
			int B = context[1];
			int C = context[2];
			int D = context[3];
			
			A = FF(A, B, C, D, X[0], 3);
			D = FF(D, A, B, C, X[1], 7);
			C = FF(C, D, A, B, X[2], 11);
			B = FF(B, C, D, A, X[3], 19);
			A = FF(A, B, C, D, X[4], 3);
			D = FF(D, A, B, C, X[5], 7);
			C = FF(C, D, A, B, X[6], 11);
			B = FF(B, C, D, A, X[7], 19);
			A = FF(A, B, C, D, X[8], 3);
			D = FF(D, A, B, C, X[9], 7);
			C = FF(C, D, A, B, X[10], 11);
			B = FF(B, C, D, A, X[11], 19);
			A = FF(A, B, C, D, X[12], 3);
			D = FF(D, A, B, C, X[13], 7);
			C = FF(C, D, A, B, X[14], 11);
			B = FF(B, C, D, A, X[15], 19);
			
			A = GG(A, B, C, D, X[0], 3);
			D = GG(D, A, B, C, X[4], 5);
			C = GG(C, D, A, B, X[8], 9);
			B = GG(B, C, D, A, X[12], 13);
			A = GG(A, B, C, D, X[1], 3);
			D = GG(D, A, B, C, X[5], 5);
			C = GG(C, D, A, B, X[9], 9);
			B = GG(B, C, D, A, X[13], 13);
			A = GG(A, B, C, D, X[2], 3);
			D = GG(D, A, B, C, X[6], 5);
			C = GG(C, D, A, B, X[10], 9);
			B = GG(B, C, D, A, X[14], 13);
			A = GG(A, B, C, D, X[3], 3);
			D = GG(D, A, B, C, X[7], 5);
			C = GG(C, D, A, B, X[11], 9);
			B = GG(B, C, D, A, X[15], 13);
			
			A = HH(A, B, C, D, X[0], 3);
			D = HH(D, A, B, C, X[8], 9);
			C = HH(C, D, A, B, X[4], 11);
			B = HH(B, C, D, A, X[12], 15);
			A = HH(A, B, C, D, X[2], 3);
			D = HH(D, A, B, C, X[10], 9);
			C = HH(C, D, A, B, X[6], 11);
			B = HH(B, C, D, A, X[14], 15);
			A = HH(A, B, C, D, X[1], 3);
			D = HH(D, A, B, C, X[9], 9);
			C = HH(C, D, A, B, X[5], 11);
			B = HH(B, C, D, A, X[13], 15);
			A = HH(A, B, C, D, X[3], 3);
			D = HH(D, A, B, C, X[11], 9);
			C = HH(C, D, A, B, X[7], 11);
			B = HH(B, C, D, A, X[15], 15);
			
			context[0] += A;
			context[1] += B;
			context[2] += C;
			context[3] += D;
		}
		
		// The basic MD4 atomic functions.
		
		private int FF(int a, int b, int c, int d, int x, int s)
		{
			int t = a + ((b & c) | (~ b & d)) + x;
			return t << s | SupportClass.URShift(t, (32 - s));
		}
		
		private int GG(int a, int b, int c, int d, int x, int s)
		{
			int t = a + ((b & (c | d)) | (c & d)) + x + 0x5A827999;
			return t << s | SupportClass.URShift(t, (32 - s));
		}
		
		private int HH(int a, int b, int c, int d, int x, int s)
		{
			int t = a + (b ^ c ^ d) + x + 0x6ED9EBA1;
			return t << s | SupportClass.URShift(t, (32 - s));
		}
		
		/// <summary> Bugfixed, now works prima (RST).</summary>
		public static int Com_BlockChecksum(sbyte[] buffer, int length)
		{
			
			int val;
			MD4 md4 = new MD4();
			
			md4.engineUpdate(buffer, 0, length);
			sbyte[] data = md4.engineDigest();
			ByteBuffer bb = ByteBuffer.wrap(data);
			bb.order(ByteOrder.LITTLE_ENDIAN);
			val = bb.getInt() ^ bb.getInt() ^ bb.getInt() ^ bb.getInt();
			return val;
		}
	}
}